﻿using System;
using System.Collections.Generic;
using O1;
using O1.Data;
using O1.Data.Traversal;

namespace Tests.O1.Behaviors
{
    public static class ScenarioBuilder
    {
        public static CellBuilder BuildGraphForOutboundEdgeFilter(
            uint rootCell, 
            ushort edgeTypeMatch, 
            EdgeDirections directionMatch, 
            int depth, 
            int degree,
            int matchRatio,
            out Filter[] queryFilters)
        {
            var builder = new CellBuilder((depth * degree) * (CellAddress.Size + Edge.Size));
            builder.StartVertex(new CellAddress(0, rootCell));
            for (int i = 0; i < degree; ++i)
            {
                var match = i % matchRatio == 0;
                var edgeType = (ushort)(match ? edgeTypeMatch : edgeTypeMatch + 1);
                var direction = match ? directionMatch : EdgeDirections.None;
                var nextVertex = new CellAddress((ushort)((i % 16) + 1), (uint)(rootCell + 1 + i));

                builder.StartEdge(new Edge(edgeType, nextVertex.CellId, nextVertex.TrunkId), direction);
                builder.EndEdge(ObservationInterests.Interested);
            }

            builder.EndVertex(ObservationInterests.Interested);

            var numberOfCells = (int)Math.Pow(degree, depth);
            for (int i = 0; i < numberOfCells; i++)
            {
                var thisCell = new CellAddress((ushort)(i % 16), (uint)(rootCell + 1 + i));
                builder.StartVertex(thisCell);
                for (int j = 0; j < degree; ++j)
                {
                    var match = j % matchRatio == 0;
                    var edgeType = (ushort)(match ? edgeTypeMatch : 0 - (j % matchRatio));
                    var direction = match ? directionMatch : EdgeDirections.None;
                    var nextVertex = new CellAddress((ushort)(i % 16), (uint)(rootCell + i + j));

                    builder.StartEdge(new Edge(edgeType, nextVertex.CellId, nextVertex.TrunkId), direction);
                    builder.EndEdge(ObservationInterests.Interested);
                }

                builder.EndVertex(ObservationInterests.Interested);
            }

            var inclusions = new EdgeSet(1);
            inclusions.Add(directionMatch, edgeTypeMatch);
            EdgeFilter[] filters = new EdgeFilter[] { new EdgeFilter(inclusions) };
            queryFilters = BuildFilteredVertexProjections(filters, depth, degree / matchRatio);
            return builder;
        }

        private static TraversalProjection[] BuildFilteredVertexProjections(EdgeFilter[] filters, int depth, int degreeFactor)
        {
            var projections = new TraversalProjection[depth];
            for(int i = 0; i < depth; ++i)
            {
                if (filters != null && filters.Length > i)
                {
                    projections[i] = new TraversalProjection(filters[i], degreeFactor * (i + 1));
                }
                else
                {
                    projections[i] = new TraversalProjection(degreeFactor * (i + 1));
                }
            }

            return projections;
        }

        private static void AddEdges(CellBuilder builder, int degree, int matchFactor, short edgeTypeMatch, EdgeDirections directionMatch)
        {
        }

        private static EdgeFilter BuildFilter(ushort[] includedEdgeTypes, ushort[] excludedEdgeTypes)
        {
            var inclusions = BuildEdgeSet(includedEdgeTypes, EdgeDirections.Outbound);
            var exclusions = BuildEdgeSet(excludedEdgeTypes, EdgeDirections.Outbound);
            return new EdgeFilter(inclusions, exclusions);
        }

        private static EdgeSet BuildEdgeSet(ushort[] edgeTypes, EdgeDirections direction)
        {
            if (edgeTypes != null && edgeTypes.Length > 0)
            {
                var set = new EdgeSet(edgeTypes.Length);
                for (int i = 0; i < edgeTypes.Length; ++i)
                {
                    set.Add(EdgeDirections.Outbound, edgeTypes[i]);
                }

                return set;
            }

            return null;
        }

        ////public static void BuildRelatedVertex(
        ////    CellBuilder builder,
        ////    CellAddress addr1,
        ////    CellAddress addr2,
        ////    short edgeType,
        ////    EdgeDirections direction)
        ////{
        ////    builder.StartVertex(addr2);
        ////    builder.StartEdge(new Edge(edgeType, addr1.CellId, addr1.TrunkId), direction);
        ////    builder.EndEdge(ObservationInterests.Interested);
        ////    builder.EndVertex(ObservationInterests.Interested);
        ////}

        ////public static void Build2RelatedCellsWithDateVertexAndEdgeProperties(
        ////    CellBuilder builder,
        ////    CellAddress addr1, 
        ////    DateProperty vprop1, 
        ////    DateProperty eprop1, 
        ////    CellAddress addr2, 
        ////    short edgeType,
        ////    DateProperty vprop2, 
        ////    DateProperty eprop2)
        ////{
        ////    builder.StartVertex(addr1);
        ////    builder.Evaluate(vprop1);
        ////    builder.StartEdge(new Edge(edgeType, addr2.CellId, addr2.TrunkId), EdgeDirections.Outbound);
        ////    builder.Evaluate(eprop1);
        ////    builder.EndEdge(ObservationInterests.Interested);
        ////    builder.EndVertex(ObservationInterests.Interested);
        ////    builder.StartVertex(addr2);
        ////    builder.Evaluate(vprop2);
        ////    builder.StartEdge(new Edge(edgeType, addr1.CellId, addr1.TrunkId), EdgeDirections.Inbound);
        ////    builder.Evaluate(eprop2);
        ////    builder.EndEdge(ObservationInterests.Interested);
        ////    builder.EndVertex(ObservationInterests.Interested);
        ////}
    }
}
